﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.Mvc;
using System.Web.Routing;
using Xoohoo.ViewEngines;

namespace Xoohoo.Extensions
{
    public static class UrlHelperExtensions
    {
        /// <summary>
        /// 构建绝对路径
        /// </summary>
        /// <param name="urlHelper">UrlHelper</param>
        /// <param name="relativeUrl">相对路径</param>
        /// <returns></returns>
        public static string AbsolutePath(this UrlHelper urlHelper, string relativeUrl)
        {
            Uri url = urlHelper.RequestContext.HttpContext.Request.Url;
            UriBuilder uriBuilder = new UriBuilder(url.Scheme, url.Host, url.Port) { Path = relativeUrl };

            string path = uriBuilder.Uri.ToString();

            if (path.EndsWith("/"))
                path = path.Substring(0, path.Length - 1);

            //TODO: (erikpo) Instead of this workaround, chop off the hash before feeding the url to UrlBuilder, then tack it back on before returning the final url
            path = path.Replace("%23", "#");
            return path;
        }

        /// <summary>
        /// 构建应用程序路径
        /// </summary>
        /// <param name="urlHelper">UrlHelper</param>
        /// <param name="relativeUrl">相对路径(或绝对路径)</param>
        /// <returns></returns>
        public static string AppPath(this UrlHelper urlHelper, string relativePath)
        {
            if (relativePath == null) return null;

            if (relativePath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) 
                || relativePath.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
                return relativePath;

            if (!relativePath.StartsWith("~/"))
            {
                if (!relativePath.StartsWith("/"))
                    relativePath = "/" + relativePath;
                if (!relativePath.StartsWith("~"))
                    relativePath = "~" + relativePath;
            }

            return VirtualPathUtility.ToAbsolute(relativePath, urlHelper.RequestContext.HttpContext.Request.ApplicationPath);
        }

        /// <summary>
        /// 构建样式表文件路径
        /// </summary>
        /// <param name="urlHelper">UrlHelper</param>
        /// <param name="relativeUrl">相对路径</param>
        /// <param name="viewContext">ViewContext</param>
        /// <returns></returns>
        public static string StylePath(this UrlHelper urlHelper, string relativePath, ViewContext viewContext)
        {
            string path = relativePath;

            if (!string.IsNullOrEmpty(path) && !path.StartsWith("/"))
                path = "/" + path;

            urlHelper.FilePath(viewContext, (ve, p) => ve.FindFile(p), ref path);

            return path;
        }

        /// <summary>
        /// 构建脚本文件路径
        /// </summary>
        /// <param name="urlHelper"></param>
        /// <param name="relativePath"></param>
        /// <param name="viewContext"></param>
        /// <returns></returns>
        public static string ScriptPath(this UrlHelper urlHelper, string relativePath, ViewContext viewContext)
        {
            string path = relativePath;

            if (!string.IsNullOrEmpty(path) && !path.StartsWith("/"))
                path = "/" + path;

            urlHelper.FilePath(viewContext, (ve, p) => ve.FindFile(p), ref path);

            return path;
        }

        /// <summary>
        /// 构建文件路径
        /// </summary>
        /// <param name="urlHelper">UrlHelper</param>
        /// <param name="viewContext">ViewContext</param>
        /// <param name="findFile">要查找的文件</param>
        /// <param name="path">要返回的路径</param>
        internal static void FilePath(this UrlHelper urlHelper, ViewContext viewContext, Func<IXoohooViewEngine, string, FileEngineResult> findFile, ref string path)
        {
            List<string> searchedLocations = new List<string>(50);

            //key为XoohooViewEngines的ViewData是通过ViewEnginesResultFilter设置的
            foreach (IXoohooViewEngine viewEngine in (IEnumerable<IXoohooViewEngine>)viewContext.ViewData["XoohooViewEngines"])
            {
                FileEngineResult result = findFile(viewEngine, path);

                if (result.SearchedLocations.Count() > 0)
                    searchedLocations.AddRange(result.SearchedLocations);
                else
                {
                    path = urlHelper.AppPath(result.FilePath);
                    searchedLocations.Clear();

                    break;
                }
            }

            if (searchedLocations.Count > 0)
            {
                //如果是调试模式，如果没找到文件，抛出异常
                //key为Debug的ViewData是通过DebugActionFilter设置的
                if (viewContext.ViewData["Debug"] is bool && (bool)viewContext.ViewData["Debug"])
                {
                    StringBuilder locationsText = new StringBuilder();

                    foreach (string location in searchedLocations)
                    {
                        locationsText.AppendLine();
                        locationsText.Append(location);
                    }

                    throw new InvalidOperationException(string.Format("The file '{0}' could not be found. The following locations were searched:{1}", path, locationsText));
                }
                else
                {
                    path = urlHelper.AppPath(searchedLocations.ElementAt(0));
                }
            }
        }

        /// <summary>
        /// 站点首页
        /// </summary>
        /// <param name="urlHelper">UrlHelper</param>
        /// <returns></returns>
        public static string Home(this UrlHelper urlHelper)
        {
            return urlHelper.AppPath("~/");
        }

        /// <summary>
        /// 缩略网址
        /// </summary>
        /// <param name="urlHelper"></param>
        /// <param name="absoluteUrlEncoded"></param>
        /// <returns></returns>
        public static string CompressUrl(this UrlHelper urlHelper, string absoluteUrlEncoded)
        {
            string cacheKey = "tinyurl:" + absoluteUrlEncoded.ToLower();
            Cache cache = urlHelper.RequestContext.HttpContext.Cache;
            string url = (string)cache[cacheKey];

            if (string.IsNullOrEmpty(url))
            {
                try
                {
                    const string urlToSendTo = "http://is.gd/api.php?longurl={0}";
                    WebClient wc = new WebClient();

                    url = wc.DownloadString(string.Format(urlToSendTo, absoluteUrlEncoded));

                    cache.Add(cacheKey, url, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
                }
                catch
                {
                    url = absoluteUrlEncoded;
                }
            }

            return url;
        }
        
        public static string ActionEx(this UrlHelper helper, string action, object routeValues)
        {
            var values = routeValues == null ?
                new RouteValueDictionary() :
                new RouteValueDictionary(routeValues);
            values.Add("action", action);
            values.Add("controller", helper.RequestContext.RouteData.Values["controller"]);

            var pathData = helper.RouteCollection.GetPath(helper.RequestContext, values);
            var url = pathData.VirtualPath;
            return IsAbsolute(url) ? url : "/" + url;
        }

        #region Private Methods

        private static VirtualPathData GetPath(
            this RouteCollection routes,
            RequestContext requestContext,
            RouteValueDictionary values)
        {
            foreach (RouteBase r in routes)
            {
                VirtualPathData pathData = r.GetVirtualPath(requestContext, values);
                if (pathData != null)
                {
                    return pathData;
                }
            }

            throw new ArgumentException("Invalid values for building URL.");
        }

        private static bool IsAbsolute(string url)
        {
            return
                url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) ||
                url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase);
        }
        #endregion

    }
}
